package top.go2do.auth.business.user.service.decorator;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.StringUtils;
import top.go2do.auth.business.captcha.service.CaptchaService;
import top.go2do.auth.business.user.module.LoginCheckBO;
import top.go2do.auth.business.user.module.LoginResultVO;
import top.go2do.common.module.UserPO;
import top.go2do.auth.business.user.service.LoginService;
import top.go2do.auth.configProperties.AuthModuleConfig;
import top.go2do.auth.exception.AuthErrorCode;
import top.go2do.auth.exception.AuthException;

import java.util.concurrent.TimeUnit;

import static top.go2do.common.redis.RedisKey.getCaptchaIdKey;
import static top.go2do.common.redis.RedisKey.getPasswordErrorKey;

/**
 * @Description
 * @Author xingyj
 * @CreateTime 2025/4/1 17:52
 */
public class CaptchaLoginDecorator extends AbstractLoginDecorator{

    private RedisTemplate redisTemplate;

    private AuthModuleConfig authConfig;

    private CaptchaService captchaService;

    public CaptchaLoginDecorator(LoginService loginService, CaptchaService captchaService, AuthModuleConfig authConfig, RedisTemplate redisTemplate) {
        super(loginService);
        this.captchaService = captchaService;
        this.authConfig = authConfig;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public LoginResultVO toLoginWithToken(LoginCheckBO checkBO) {
        String username = checkBO.getUsername();
        String appId = checkBO.getAppId();
        UserPO userPO = super.findByAppIdAndUserName(appId, username);
        if (userPO == null) {
            throw new AuthException(AuthErrorCode.USERNAME_NOT_EXIST);
        }
        Long userId = userPO.getUserId();
        int pwdErrorCount = getPasswordErrorTimes(userId);
        if (pwdErrorCount >= authConfig.getPwdErrorMaxTimes()) {
            // 一段时间内密码验证错误次数超过上限，则锁定用户一段时间、不能继续验证
            throw new AuthException(AuthErrorCode.USER_LOCKED);
        } else if (pwdErrorCount >= authConfig.getCaptchaErrorTimes()) {
            // 密码验证已经超过设置次数，需要验证图形码
            String captchaId = checkBO.getCaptchaId();
            if (StringUtils.hasText(captchaId)) {
                String serverCaptchaCode = getCaptchaCode(captchaId);
                if (StringUtils.hasText(serverCaptchaCode)) {
                    // 如果服务端该用户名的验证码已存在，则校验客户端验证码
                    String clientCaptchaCode = checkBO.getCaptchaCode();
                    if (!StringUtils.hasText(clientCaptchaCode)) {
                        // 客户端验证码未传递
                        throw new AuthException(AuthErrorCode.USER_LOGIN_NO_CAPTCHA);
                    }
                    if (!clientCaptchaCode.equalsIgnoreCase(serverCaptchaCode)) {
                        // 客户端验证码错误
                        throw new AuthException(AuthErrorCode.USER_LOGIN_CAPTCHA_ERROR);
                    } else {
                        // 验证码校验通过，先删除验证码，然后开始校验登录密码
                        delCaptchaCode(captchaId);
                        return loginAndRecordPasswordError(checkBO, userId);
                    }
                } else {
                    // 如果服务端对应id的验证码不存在，需要提示客户端获取新的captcha
                    throw new AuthException(AuthErrorCode.USER_LOGIN_NEED_CAPTCHA);
                }
            } else {
                // 未携带captchaId,需要提示客户端获取captcha
                throw new AuthException(AuthErrorCode.USER_LOGIN_NEED_CAPTCHA);
            }
        } else {
            // 未超过次数，不需要校验图形验证码，直接校验登录密码
            return loginAndRecordPasswordError(checkBO, userId);
        }

    }

    private LoginResultVO loginAndRecordPasswordError(LoginCheckBO checkBO, Long userId) {
        boolean checkPassword = super.loginCheck(checkBO);
        if (!checkPassword) {
            /**
             * 记录密码错误次数
             */
            int count = recordPasswordError(userId);
            if (count >= authConfig.getPwdErrorMaxTimes()) {
                throw new AuthException(AuthErrorCode.USER_LOCKED);
            } else {
                throw new AuthException(AuthErrorCode.USER_PASSWORD_ERROR);
            }
        }
        return super.generateLoginResult(userId);
    }

    private int recordPasswordError(Long userId) {
        Integer timeoutSecond = authConfig.getTimeoutSecond(), errorTimes = authConfig.getPwdErrorMaxTimes();
        String key = getPasswordErrorKey(userId);
        ValueOperations operations = redisTemplate.opsForValue();
        boolean result = operations.setIfAbsent(key, 1, timeoutSecond, TimeUnit.SECONDS);
        int count = 1;
        if (!result) {
            // 设置失败，key已经存在，则累加
            count = Math.toIntExact(operations.increment(key));
            // 错误记录errorTimes次时，更新过期时间
//            if (count == errorTimes) {
//                redisTemplate.expire(key, timeoutSecond, TimeUnit.SECONDS);
//            }
            redisTemplate.expire(key, timeoutSecond, TimeUnit.SECONDS);
        }
        return count;
    }

    private int getPasswordErrorTimes(Long userId) {
        Object result = redisTemplate.opsForValue().get(getPasswordErrorKey(userId));
        return result == null ? 0: Integer.parseInt(String.valueOf(result));
    }

    private String getCaptchaCode(Long userId) {
        Object result = redisTemplate.opsForValue().get(getCaptchaIdKey(userId));
        return result == null ? "" : String.valueOf(result);
    }

    private String getCaptchaCode(String captchaId) {
        Object result = redisTemplate.opsForValue().get(getCaptchaIdKey(captchaId));
        return result == null ? "" : String.valueOf(result);
    }

    private void setCaptchaCode(Long userId, String code) {
        redisTemplate.opsForValue().set(getCaptchaIdKey(userId), code, authConfig.getCaptchaTimeoutSecond(), TimeUnit.SECONDS);
    }

    private void delCaptchaCode(Long userId) {
        redisTemplate.delete(getCaptchaIdKey(userId));
    }

    private void delCaptchaCode(String captchaId) {
        redisTemplate.delete(getCaptchaIdKey(captchaId));
    }
}
